/*
 * Copyright (C) 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.caliper.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * Resource manager.
 */
public class Resources {

	protected final static int EOF = -1;

	/**
	 * Location of allocator jar inside caliper jar. Managed by pom.xml
	 */
	public static final String RESOUCRE_ALLOCATION_JAR = "/lib/allocation.jar";

	/**
	 * Name of temporary folder used by {@link #ensureTempFile(String)} 
	 */
	public static final String NAME_TEMP_PATH = "google-caliper";

	/**
	 * Extract {@link #RESOUCRE_ALLOCATION_JAR} to provided external location
	 * 
	 * @param targetPath - absolute path on local file system
	 */
	public static void ensureAllocationJar(final String targetPath)
			throws Exception {

		extractResource(RESOUCRE_ALLOCATION_JAR, targetPath);
		
	}

	public static File ensureTempFile(final String name) throws Exception {

		final File tmpDir = new File(System.getProperty("java.io.tmpdir"));

		final File folder = new File(tmpDir, NAME_TEMP_PATH);
		if (!folder.exists()) {
			folder.mkdirs();
		}

		final File file = new File(folder, name);
		if (!file.exists()) {
			file.createNewFile();
		}

		return file.getAbsoluteFile();

	}

	
	/**
	 * Extract resource from class path to external location
	 * 
	 * @param sourcePath - resource path on class path
	 * @param targetPath - resource URL on local file system
	 */
	public static void extractResource(final String sourcePath,
			final String targetPath) throws Exception {

		final URL sourceUrl = Resources.class.getResource(sourcePath);

		if (sourceUrl == null) {
			throw new IllegalArgumentException("resource not found: "
					+ sourcePath);
		}

		final URLConnection sourceConn = sourceUrl.openConnection();

		if (sourceConn == null) {
			throw new IllegalArgumentException("resource not found: "
					+ sourcePath);
		}

		final File targetFile = new File(targetPath).getAbsoluteFile();

		final File targetFolder = targetFile.getParentFile().getAbsoluteFile();

		ensureTargetFolder(targetFolder);

		final URLConnection targetConn = fileConnection(targetFile);

		if (isSameResource(sourceConn, targetConn)) {
			return;
		} else {
			targetFile.delete();
			targetFile.createNewFile();
		}

		final long sourceTime = timeStamp(sourceConn);

		final InputStream sourceStream = new BufferedInputStream(//
				sourceUrl.openStream());

		final OutputStream targetStream = new BufferedOutputStream(//
				new FileOutputStream(targetFile));

		final byte[] array = new byte[8 * 1024];

		int readCount = 0;

		while ((readCount = sourceStream.read(array)) != EOF) {
			targetStream.write(array, 0, readCount);
		}

		targetStream.flush();

		sourceStream.close();
		targetStream.close();

		/** synchronize target time stamp with source to avoid repeated copy */
		targetFile.setLastModified(sourceTime);

	}

	public static boolean isSameResource(final URLConnection conONE,
			final URLConnection conTWO) throws Exception {

		final long timeONE = conONE.getLastModified();
		final long sizeONE = conONE.getContentLength();

		final long timeTWO = conTWO.getLastModified();
		final long sizeTWO = conTWO.getContentLength();

		return sizeONE == sizeTWO && timeONE == timeTWO;

	}

	public static void ensureTargetFolder(final String targetFolder)
			throws Exception {

		final File folder = new File(targetFolder).getAbsoluteFile();

		ensureTargetFolder(folder);

	}

	public static void ensureTargetFolder(final File folder) throws Exception {
		if (folder.exists()) {
			if (folder.isDirectory()) {
			} else {
				throw new IllegalArgumentException(
						"extract destination exists, but as a file and not a folder");
			}
		} else {
			final boolean isSuccess = folder.mkdirs();
			if (isSuccess) {
			} else {
				throw new IllegalStateException(
						"failed to make extract destination folder");
			}
		}
	}

	public static URLConnection fileConnection(final File file)
			throws Exception {

		final URL url = file.toURI().toURL();

		final URLConnection connection = url.openConnection();

		return connection;

	}

	/**
	 * note: will use time stamp of jar file for "file://" url
	 */
	public static long timeStamp(final URLConnection connIN) {
		return connIN.getLastModified();
	}

	private Resources() {
	}

}
